﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NFit.Core.Interfaces;
using log4net;
using NFit.Core.Config;

namespace NFit.Core
{
    /// <summary>
    /// NFit Runner implementation
    /// </summary>
    [Serializable]
    public class BaseRunner : INFitRunner
    {
        private static ILog Logger = log4net.LogManager.GetLogger(typeof(BaseRunner));

        private ISpecificationExecution _specificationExec;

        private NFitConfiguration _config = new NFitConfiguration();
        /// <summary>
        /// Configuration
        /// </summary>
        public NFitConfiguration Config
        {
            get
            {
                return _config;
            }
        }

        /// <summary>
        /// The module responsible for executing a specification
        /// </summary>
        public ISpecificationExecution SpecificationExec
        {
            get
            {
                if (_specificationExec == null)
                    _specificationExec = ServiceLocator.Resolve<ISpecificationExecution>();

                return _specificationExec;
            }
        }

        ISpecificationExecution INFitRunner.SpecificationExec { get; set; }

        /// <summary>
        /// Execute all the specifications in the provided datasources and give an execution summary
        /// </summary>
        /// <returns></returns>
        public ExecutionSummary Run()
        {
            IExecutionContext Context = ServiceLocator.Resolve<IExecutionContext>();
            ExecutionSummary summary = new ExecutionSummary();
            foreach(var datasource in Config.Datasources)
            {
                RunSource(Context, null, datasource.GetTestDefinitions(), summary);
            }
            summary.ExecutionEnd = DateTime.Now;
            
            if (Config.Writers != null)
            {
                foreach (var writer in Config.Writers)
                {
                    Logger.Debug("Writing to " + writer.GetType());
                    writer.Report(summary);
                }
            }
            return summary;
        }

        private void RunSource(IExecutionContext Context, SpecificationDefinition parentSpec, IEnumerable<SpecificationDefinition> source, ExecutionSummary summary)
        {
            foreach (var spec in source)
            {
                RunSpec(Context, parentSpec, spec, summary);
            }
        }

        private void RunSpec(IExecutionContext Context, SpecificationDefinition parentSpec, SpecificationDefinition spec, ExecutionSummary summary)
        {
            Logger.Debug("running spec : " + spec.Name);
            SpecificationExec.Run(Context, spec);

            if (spec.IsSuccessful)
                summary.IncreaseSuccessful();
            else
                summary.IncreaseFailed();

            if (parentSpec == null)
                summary.RunnedSpecs.Add(spec);

            RunSource(Context, spec, spec.ChildSpecs, summary);
        }

    }
}
